; Disassembly of the file "z:\ramdisk\home\knoppix\none\CPM_CHRIS_Loader.bin" ; ; CPU Type: Z80 ; ; Created with dZ80 2.0 ; ; on Sunday, 14 of October 2012 at 06:36 PM ; 0000 00 NOP ; first byte of CP/M loader must be $00 (!= $F3) 0001 F3 DI 0002 E5 PUSH HL ; HL=$0000 from the BOOT ROM (end of CP/M loading routine) 0003 3EC9 LD A,$C9 ; $C9 = opcode for RET instruction of Z80 0005 320000 LD ($0000),A ; replace first byte of this loader with RET instruction 0008 CD0000 CALL $0000 ; $000B is saved on stack when this call executed ... 000B 3B DEC SP 000C 3B DEC SP 000D E1 POP HL ; ... so now HL=$000B (nice stupid wiseass trick, big deal ...) 000E 3E41 LD A,$41 ; 0010 D3FE OUT ($FE),A ; write 41 to port C of 8255 ; ; (set border to blue, set signal "SO" to 0, set signal "O5" to 0, ; ; set signal "O6" to 1 to allow access to video memory in the CP/M hw config) ; ############## DISPLAY THE "PLUS" LOGO ############### 0012 013000 LD BC,$0030 0015 09 ADD HL,BC ; HL=$003B 0016 11005A LD DE,$5A00 0019 0620 LD B,$20 001B C5 PUSH BC ; BC=$20xx <-- <--- <--- <--- <--- <--- <--- <--| 001C 4E LD C,(HL) ; C=($003B)=$00 | 001D 0608 LD B,$08 ; 8 loops next | 001F 3E00 LD A,$00 ; <--- <--- <--- <--- <--- <--- <--- <--| | 0021 CB01 RLC C ; Carry Flag is 0 | | 0023 3002 JR NC,$0027; jump if bit=0 --->| | | 0025 3E12 LD A,$12 ; | | | 0027 12 LD (DE),A ; <--- <--- <--- <---| ($5A00)=$00 | | 0028 13 INC DE ; DE=$5A01 | | 0029 10F4 DJNZ $001F ; ---> ---> ---> ---> ---> ---> ---> -->| | 002B C1 POP BC ; BC=$20xx | 002C 23 INC HL ; HL=$003C | 002D 10EC DJNZ $001B ; ---> ---> ---> ---> ---> ---> ---> ---> ----->| 32 loops ($20) 002F 184A JR $007B ; ############## UNUSED AREA ################### 0031 00 DEFB $00 0032 00 DEFB $00 0033 00 DEFB $00 0034 00 DEFB $00 0035 00 DEFB $00 0036 00 DEFB $00 0037 00 DEFB $00 0038 00 DEFB $00 0039 00 DEFB $00 003a 00 DEFB $00 ; ############################################## ; ############## DATA START #################### ; this block is used to generate the PLUS logo while loading CP/M 003B 00 DEFB $00 ; 5A00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 003C 00 DEFB $00 ; 5A08: 00 00 00 00 00 00 00 00 003D 00 DEFB $00 ; 5A10: 00 00 00 00 00 00 00 00 003E 00 DEFB $00 ; 5A18: 00 00 00 00 00 00 00 00 003F 07 DEFB $07 ; 5A20: 00 00 00 00 00 12 12 12 00 00 00 00 00 12 12 12 00 00 00 12 00 00 00 00 00 12 00 00 12 00 00 00 12 12 00 00 00 00 00 00 0040 10 DEFB $10 ; 5A28: 00 00 00 12 00 00 00 00 0041 48 DEFB $48 ; 5A30: 00 12 00 00 12 00 00 00 0042 C0 DEFB $C0 ; 5A38: 12 12 00 00 00 00 00 00 0043 04 DEFB $04 ; 5A40: 00 00 00 00 00 12 00 00 00 00 00 00 00 12 00 00 12 00 00 12 00 00 00 00 00 12 00 00 12 00 00 12 00 00 00 00 00 00 00 00 0044 90 DEFB $90 ; 5A48: 12 00 00 12 00 00 00 00 0045 49 DEFB $49 ; 5A50: 00 12 00 00 12 00 00 12 0046 00 DEFB $00 ; 5A58: 00 00 00 00 00 00 00 00 0047 04 DEFB $04 ; 5A60: 00 00 00 00 00 12 00 00 00 00 00 00 00 12 00 00 12 00 00 12 00 00 00 00 00 12 00 00 12 00 00 12 12 12 00 00 00 00 00 00 0048 90 DEFB $90 ; 5A68: 12 00 00 12 00 00 00 00 0049 49 DEFB $49 ; 5A70: 00 12 00 00 12 00 00 12 004A C0 DEFB $C0 ; 5A78: 12 12 00 00 00 00 00 00 004B 07 DEFB $07 ; 5A80: 00 00 00 00 00 12 12 12 00 00 00 00 00 12 12 12 00 00 00 12 00 00 00 00 00 12 00 00 12 00 00 00 00 00 12 00 00 00 00 00 004C 10 DEFB $10 ; 5A88: 00 00 00 12 00 00 00 00 004D 48 DEFB $48 ; 5A90: 00 12 00 00 12 00 00 00 004E 20 DEFB $20 ; 5A98: 00 00 12 00 00 00 00 00 004F 04 DEFB $04 ; 5AA0: 00 00 00 00 00 12 00 00 00 00 00 00 00 12 00 00 00 00 00 12 00 00 00 00 00 12 00 00 12 00 00 12 00 00 12 00 00 00 00 00 0050 10 DEFB $10 ; 5AA8: 00 00 00 12 00 00 00 00 0051 49 DEFB $49 ; 5AB0: 00 12 00 00 12 00 00 12 0052 20 DEFB $20 ; 5AB8: 00 00 12 00 00 00 00 00 0053 04 DEFB $04 ; 5AC0: 00 00 00 00 00 12 00 00 00 00 00 00 00 12 00 00 00 00 00 12 12 12 12 00 00 00 12 12 00 00 00 00 12 12 00 00 00 00 00 00 0054 1E DEFB $1E ; 5AC8: 00 00 00 12 12 12 12 00 0055 30 DEFB $30 ; 5AD0: 00 00 12 12 00 00 00 00 0056 C0 DEFB $C0 ; 5AD8: 12 12 00 00 00 00 00 00 0057 00 DEFB $00 ; 5AE0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0058 00 DEFB $00 ; 5AE8: 00 00 00 00 00 00 00 00 0059 00 DEFB $00 ; 5AF0: 00 00 00 00 00 00 00 00 005A 00 DEFB $00 ; 5AF8: 00 00 00 00 00 00 00 00 ; ############## DATA END ###################### ; ############## UNUSED AREA ################### 005b 00 DEFB $00 005c 00 DEFB $00 005d 00 DEFB $00 005e 00 DEFB $00 005f 00 DEFB $00 0060 00 DEFB $00 0061 00 DEFB $00 0062 00 DEFB $00 0063 00 DEFB $00 0064 00 DEFB $00 0065 00 DEFB $00 0066 00 DEFB $00 0067 00 DEFB $00 0068 FF DEFB $FF 0069 FF DEFB $FF 006a 00 DEFB $00 006b 00 DEFB $00 006c FF DEFB $FF 006d FF DEFB $FF 006e 00 DEFB $00 006f 00 DEFB $00 0070 FF DEFB $FF 0071 FF DEFB $FF 0072 00 DEFB $00 0073 00 DEFB $00 0074 FF DEFB $FF 0075 FF DEFB $FF 0076 00 DEFB $00 0077 00 DEFB $00 0078 FF DEFB $FF 0079 FF DEFB $FF 007a 00 DEFB $00 ; ############################################## 007B 3E01 LD A,$01 007D D3FE OUT ($FE),A ; write $01 to port C of 8255 ; ; (set border to blue, set signal "SO" to 0, set signal "O5" to 0, ; ; set signal "O6" to 0 to allow access to DRAM#1 in the hw startup config) 007F E1 POP HL ; HL=$0000, value pushed on stack at the very beginning 0080 EB EX DE,HL ; DE=$0000, HL=$5B00 0081 2A9A00 LD HL,($009A) ; HL=$0400 0084 4B LD C,E ; C=$00 0085 44 LD B,H ; B=$04 --> BC=$0400 0086 EDB8 LDDR ; copy $0400-$0001 to $0000-$FC01; when done BC=$0000, DE=$FC00, HL=$0000 0088 EB EX DE,HL ; DE=$0000, HL=$FC00 0089 CBE5 SET 4,L 008B CBFD SET 7,L ; HL=$FC90 008D E9 JP (HL) ; jump to $FC90 which is $0090 below--->| ; | 008E 00 NOP ; | 008F 00 NOP ; | 0090 1820 JR $00B2 ; <--- <---|<--- <--- <--- <--- <--- <--| <---- CTC0 Interrupt Vector (unused) ; | 0092 09 DEFB $09 ;FC92 | \ CTC1 Interrupt 0093 FD DEFB $FD ;FC93 | / Vector 0094 FC DEFB $FC ;FC94 | \ CTC2 Interrupt 0095 FC DEFB $FC ;FC95 | / Vector 0096 00 DEFB $00 ;FC96 | \ CTC3 Interrupt (not | <-- instead, here is a time constant for CTC1: $00 means 1 interrupt for $100 bytes transferred 0097 20 DEFB $20 ;FC97 | / Vector used) | <-- instead, here is a time constant for CTC2: $20 x $100 = $2000, that is 1 interrupt for 8192 bytes = both sides of a system track 0098 FF DEFB $FF ;FC98 | \ Start (top) address for saving 0099 3F DEFB $3F ;FC99 | / system track #0 data - in reverse !! 009A 00 DEFB $00 ;FC9A | \ Here TRACK_READ will save the start (top) address ($1FFF) 009B 04 DEFB $04 ;FC9B | / for saving system track #1 data in reverse, after track #0 is already saved 009C 03 DEFB $03 ;FC9C | \ 8272 Specify command opcode 009D 03 DEFB $03 ;FC9D | / 3 loops for 8272_CMD (2 args required after Specify command opcode) 009E 08 DEFB $08 ;FC9E | \ 8272 Sense Interrupt Status command opcode 009F 01 DEFB $01 ;FC9F | / 1 loop for 8272_CMD (no args required for SIS cmd) 00A0 04 DEFB $04 ;FCA0 | \ 8272 Sense Drive Status command opcode 00A1 02 DEFB $02 ;FCA1 | / 2 loops for 8272_CMD (1 arg required after Sense Drive Status command opcode) 00A2 0F DEFB $0F ;FCA2 | \ 8272 Seek command opcode 00A3 03 DEFB $03 ;FCA3 | / 3 loops for 8272_CMD (2 args required after Seek command opcode) 00A4 4A DEFB $4A ;FCA4 | \ 8272 Read ID command opcode with MFM 00A5 02 DEFB $02 ;FCA5 | / 2 loops for 8272_CMD (1 arg required after opcode) 00A6 C6 DEFB $C6 ; | \ 8272 Read Data command opcode with MFM and MT(MultiTrack) 00A7 09 DEFB $09 ; | / 9 loops for 8272_CMD (8 args required after Read Data command opcode) 00A8 EF DEFB $EF ;FCA8 | arg #1 for 8272 Specify cmd: SRT+HUT (Step Rate Time = 2 ms, Head Unload Time = 240 ms) 00A9 3F DEFB $3F ;FCA9 *** | arg #2 for 8272 Specify cmd: HLT+ND (Head Load Time = 126 ms, Non-DMA = 1) *** This is different from LK.SYS 00AA 00 DEFB $00 ;FCAA | arg #1 for 8272 Seek cmd: (HDS+DS1+DS0) HDS kept on 0 while DS1+DS2 incremented 00AB 00 DEFB $00 ;FCAB | C = cylinder number 00AC 00 DEFB $00 ;FCAC | H = head number 00AD 00 DEFB $00 ;FCAD | R = sector number 00AE 00 DEFB $00 ;FCAE | N = bytes/sector 00AF 00 DEFB $00 ; | 00B0 FF DEFB $FF ; | 00B1 FF DEFB $FF ; | ; | 00B2 F3 DI ; <--- <---| LATER USED FOR 8272 CMD RESULT STORAGE - $FCB2-$FCB8 <--- this is at $FCB2 when executed 00B3 F9 LD SP,HL ; HL=$FC90, set stack at $FC90 $FCB2-$FCB8 00B4 ED5E IM 2 ; interrupt mode 2 $FCB2-$FCB8 00B6 7C LD A,H ; A=$FC $FCB2-$FCB8 00B7 ED47 LD I,A ; I=$FC = upper half of CTC interrupt vector address $FCB2-$FCB8 00B9 7D LD A,L ; A=$90 = lower half of CTC interrupt vector address ... 00BA D3E3 OUT ($E3),A ; ... is sent to CTC channel 0 (bit 0 = 0 means Vector) ; MEANING: Interrupt Vector being used by all 4 channels!! ; Interrupt Vector = 10010cc0 where cc is the CTC channel requesting the interrupt. ; So vector for CTC0 = $90, CTC1 = $92, CTC2 = $94, CTC3 = $96 ; The upper half of addr table pointer is $FC which is $00 within this CP/M loader (after relocation, $0000 is at $FC00). 00BC 3EFF LD A,$FF ; Control word for CTC0 on next line (Enable Interrupt, Counter Mode, Rising Edge, Time Const. Follows, Reset, Control). ; MEANING: Reset, Enable Interrupts for Channel 0, a Time Constant follows. 00BE D3E3 OUT ($E3),A ; Write control word $FF to CTC channel 0 00C0 3E01 LD A,$01 ; Time constant for CTC0 on next line: ; MEANING: CTC0 generates INT for each byte transferred from 8272 to µP 00C2 D3E3 OUT ($E3),A ; Write time constant $01 to CTC channel 0 00C4 3E7B LD A,$7B ; Control word for CTC Channel 3 on next line (Disable Interrupt, Counter Mode, Rising Edge, No Time Constant Follows, Reset, Control) ; MEANING: Reset, Disable Interrupts for Channel 3 00C6 D3FB OUT ($FB),A ; Send control word to CTC Channel 3 00C8 2A92FC LD HL,($FC92) ; HL=$FD09 = interrupt vector for the routine call below (address of CTC1 Interrupt Service Routine) 00CB CD0CFD CALL $FD0C ; Call CTC1+2_INIT (deactivates CTC0 int. routine) 00CE CD4FFD CALL $FD4F ; Call 8272_READ to read command result bytes from 8272 Data Register (to make sure there's no unfinished command) 00D1 21A8FC LD HL,$FCA8 ; addr for the 2 Specify command args 00D4 ED4B9CFC LD BC,($FC9C) ; B=$03 (2 args after 8272 cmd opcode), C=$03 (8272 Specify command opcode) 00D8 CD2FFD CALL $FD2F ; Call 8272_CMD_HL to send Specify command to 8272 00DB D9 EXX ; #------------------------------# <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <---| 00DC ED4BA0FC LD BC,($FCA0) ; B=$02 (1 arg after 8272 cmd opcode), C=$04 (8272 Sense Drive Status command opcode) | 00E0 CD2CFD CALL $FD2C ; Call 8272_CMD to send Sense Drive Status command to 8272 | 00E3 CD4FFD CALL $FD4F ; Call 8272_READ to read command result bytes from 8272 Data Register | 00E6 CB6F BIT 5,A ; test bit 5 of ST3 (status of RDY signal from FDD,should be 1(?) if READY) | 00E8 D9 EXX ; #------------------------------# | 00E9 2005 JR NZ,$00F0 ; jump if FDD Ready --> -->| | 00EB 34 INC (HL) ; HL=$FCA8+2=$FCAA after the call to 8272_CMD_HL 8 lines above, ($FCAA)=00 initially=arg #1 for 8272 Seek cmd ** increment drive number ** 00EC CB96 RES 2,(HL) ; make sure HDS=0 for 8272 Seek cmd | 00EE 18EB JR $00DB ; ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> --->| 00F0 CD71FD CALL $FD71 ; <--- <--- <--- <--- <-----| call TRACK_READ, reads track 00 both sides 00F3 CD71FD CALL $FD71 ; call TRACK_READ, reads track 01 both sides 00F6 E9 JP (HL) ; ################ CTC0 Interrupt Service Routine (this reads floppy data bytes) ######## 00F7 EDA2 INI ; ----------------------- FCF7 after relocation ------------------------- 00F9 FB EI 00FA ED4D RETI ; ################ CTC2 Interrupt Service Routine ####################### 00FC E5 PUSH HL ; ----------------------- FCFC after relocation ------------------------- 00FD 2A92FC LD HL,($FC92) ; HL=$FD09 0100 2290FC LD ($FC90),HL ; ($FC90) = CTC0 Interrupt Service Routine addr = $FD09 = CTC1 Interrupt Service Routine (does nothing) *** this deactivates CTC0 routine *** 0103 21F0FC LD HL,$FCF0 0106 36FF LD (HL),$FF ; ($FCF0)=$FF mystery byte %%%%%%%%%%%%% ??????????????? MODIFY CODE ???????????????? %%%%%%%%%%%%%% 0108 E1 POP HL ; ################ CTC1 Interrupt Service Routine (does nothing) ######## 0109 FB EI ; ----------------------- FD09 after relocation ------------------------- 010A ED4D RETI ; ############################ CTC1+2_INIT ############################## 010C F3 DI ; ----------------------- FD0C after relocation ------------------------- 010D 2290FC LD ($FC90),HL ; set CTC0 interrupt vector to value in HL ($FD09 = CTC1 Interrupt Service Routine, does nothing) *** this deactivates CTC0 routine *** 0110 3E7F LD A,$7F ; Control word for CTC1 below (Disable Interrupt, Counter Mode, Rising Edge, Time Constant Follows, Reset, Control) ; MEANING: Reset, Disable Interrupts for Channel 1 0112 D3EB OUT ($EB),A ; Send control word to CTC channel 1 0114 3A96FC LD A,($FC96) ; A=$00 = time constant for CTC1 below ; MEANING: Channel 1 counts every 256 bytes transferred 0117 D3EB OUT ($EB),A ; Send time constant to CTC channel 1 0119 3EFF LD A,$FF ; Control word for CTC2 below (Enable Interrupt, Counter Mode, Rising Edge, Time Constant Follows, Reset, Control) ; MEANING: Reset, Enable Interrupts for Channel 2 011B D3F3 OUT ($F3),A ; Send control word to CTC channel 2 011D 3A97FC LD A,($FC97) ; A=$20 = time constant for CTC2 below ; MEANING: Channel 2 counts every $20 x $100 = $2000 = 8192 bytes transferred from 8272 (both sides of a system track, each having 1 sector with 4096 bytes) 0120 D3F3 OUT ($F3),A ; Send time constant to CTC channel 2 0122 C9 RET ; ############################ 8272_POLL ################################ ; ----------------------- FD23 after relocation ------------------------- 0123 DBF5 IN A,($F5) ; read 8272 Status Register <--- <--- <--- <--- <--- <-----| | this routine polls the 8272 Status Register 0125 CB7F BIT 7,A ; test bit 7 (RQM) to see if 8272 ready to send or receive | | until 8272 is ready to send or receive 0127 28FA JR Z,$0123 ; jump if 8272 not ready ---> ---> ---> ---> ---> ---> --->| | data to or from the CPU. When ready (RQM=1) 0129 CB77 BIT 6,A ; test bit 6 (DIO) to determine direction of data transfer | bit 6 is tested (DIO, 0=send/1=receive) 012B C9 RET ; ############################ 8272_CMD ################################## ; ----------------------- FD2C after relocation ------------------------- ; BC = parameter to this subroutine: B = loop count for DJNZ $0137, C = command opcode to write to 8272 Data Register 012C 21AAFC LD HL,$FCAA ; address of 8272 command parameter bytes following the command opcode ; ############################ 8272_CMD_HL ############################### ; ----------------------- FD2F after relocation ------------------------- ; this entry point requires HL = additional parameter as address of 8272 command parameter bytes following the command opcode 012F DBF5 IN A,($F5) ; read 8272 Status Register <----| 0131 CB67 BIT 4,A ; test bit 4 (FDC Busy) | 0133 20FA JR NZ,$012F; if FDC Busy jump ---> ---> --->| 0135 1802 JR $0139 ; ---> ---> --->| 0137 4E LD C,(HL) ; <---- <--- <--- <--- <--- <---| C = (next) 8272 cmd parameter byte 0138 23 INC HL ; | | increment 8272 cmd parameter pointer 0139 F3 DI ; <--- <--- <---| | 013A CD23FD CALL $FD23 ; Call 8272_POLL | 013D 2022 JR NZ,$0161; jump to CPM_TO_COBRA if transfer is TO CPU 013F 59 LD E,C ; E=8272 cmd byte | <<< this will leave E'=$02 when 8272_CMD is called from TRACK_READ at $0197 within this binary file 0140 0EFD LD C,$FD ; 8272 Data Register port addr | since the second command byte of Read ID will be $02 (CP/M boots from drive 2, head 0) 0142 ED59 OUT (C),E ; write to 8272 Data Register | (for later when CP/M starts with BIOS function 00 BOOT !!!!) 0144 10F1 DJNZ $0137 ; ----> ---> ---> ---> ---> --->| <<< this will leave C'=$02 when 8272_CMD is called from TRACK_READ at $0197 within this binary file 0146 FB EI ; since the second command byte of Read ID will be $02 (CP/M boots from drive 2, head 0) 0147 C9 RET ; (for later when CP/M starts with BIOS function 00 BOOT !!!!) ; ############################ 8272_CMD_SIS ############################## ; ----------------------- FD48 after relocation ------------------------- 0148 ED4B9EFC LD BC,($FC9E) ; C=08=Sense Interrupt Status opcode, B=01=0 args req'd 014C CD2FFD CALL $FD2F ; Call 8272_CMD_HL to send Sense Interrupt Status cmd ; ############################ 8272_READ ################################# ; ----------------------- FD4F after relocation ------------------------- ; Read command result bytes from 8272 Data Register and store them at FCB2-FCB8. ; Normally max. 7 command result bytes can be read and normal exit is by jump at 0157 ; If after 7 bytes read the transfer direction detected does not change to "CPU -> 8272" ; then something is wrong and a jump is made to BOOT hw config (JR Z,$016C) 014F 21B2FC LD HL,$FCB2 0152 0608 LD B,$08 ; 8 loops next 0154 CD23FD CALL $FD23 ; Call 8272_POLL<--- <--- <--- <--- <---| 0157 2813 JR Z,$016C ; Jump if transfer is FROM CPU -----> ---> ---> --->| 0159 36FF LD (HL),$FF ; ($FCB2+nn)=FF | | 015B DBFD IN A,($FD) ; read 1 byte from 8272 Data Register | | 015D 77 LD (HL),A ; store byte at ($FCB2+nn) | | 015E 23 INC HL ; | | 015F 10F3 DJNZ $0154 ; ---> ---> ---> ---> ---> ---> ---> -->| | <<< this should leave B'=$01 for later when CP/M starts with BIOS function 00 BOOT !!!! 0161 F3 DI ; ############## CPM_TO_COBRA ############## | (7 result bytes are read after Read ID command) 0162 AF XOR A ; A=00 | 0163 6F LD L,A ; | 0164 67 LD H,A ; HL=0000 | 0165 F6C1 OR $C1 ; A=$C1 | 0167 D3FE OUT ($FE),A ; write $C1 to port C of 8255 | ; ; (set border to blue, set signal "SO" to 1, set signal "O5" to 0, ; ; set signal "O6" to 1 to allow access to video memory in the hw startup config) 0169 ED4F LD R,A ; set bit 7 of R to 1 to prepare jump to startup (COBRA BOOT) hw config 016B E9 JP (HL) ; jump to (HL)=$0000 | ; | 016C 21B2FC LD HL,$FCB2 ; <--- <--- <--- <--- <--- <--- <--- <--- <--- <---| <<< this leaves HL'=$FCB2 for later when CP/M starts with BIOS function 00 BOOT !!!! 016F 7E LD A,(HL) ; Load first command result byte in A 0170 C9 RET ; ############################ TRACK_READ ################################ ; ----------------------- FD71 after relocation ------------------------- 0171 ED4BA2FC LD BC,($FCA2) ; B=$03 (2 args after 8272 cmd opcode), C=$0F (8272 Seek command opcode) 0175 CD2CFD CALL $FD2C ; Call 8272_CMD to send Seek command to 8272 0178 CD48FD CALL $FD48 ; Call 8272_CMD_SIS <--- <--- <--- <--- <--- <--- <-| 017B CB6F BIT 5,A ; Test bit 5 of result ST0, SE byte = Seek End | 017D 28F9 JR Z,$0178 ; Jump if Seek not finished ---> ---> ---> ---> --->| 017F E650 AND $50 ; 0181 20DE JR NZ,$0161 ; Jump to CPM_TO_COBRA if HD or US0 are 1 0183 CD48FD CALL $FD48 ; Call 8272_CMD_SIS <--- <--- <---| 0186 FE80 CP $80 ; Check if Invalid Command issue | (check if SIS ended abnormally) 0188 20F9 JR NZ,$0183 ; jump if not ---> ---> ---> --->| ; *** the above loop goes until SIS becomes Invalid Command because it does not immediately follow a Seek or Recalibrate command ; *** it is used as No-Op command, to place the FDC in a standby or no operation state. 018A DBF5 IN A,($F5) ; read 8272 Status Register <--- <--- <--- <-----| 018C E60F AND $0F ; Check if any FDD is in Seek Mode (Main Status Register bits 0-3) 018E 20FA JR NZ,$018A ; jump if any FDD is in Seek Mode ---> ---> --->| 0190 0603 LD B,$03 ; loop counter - next loop goes 3 times 0192 D9 EXX ; <---- <--- <--- <--- <--- <---| 0193 ED4BA4FC LD BC,($FCA4) ; B=$02 (1 arg after 8272 cmd opcode), C=$4A (8272 Read ID command opcode with MFM) 0197 CD2CFD CALL $FD2C ; Call 8272_CMD to send Read ID command to 8272 019A CD4FFD CALL $FD4F ; Call 8272_READ and save | Read ID result bytes at $FCB2-$FCB8 019D E6C0 AND $C0 ; Check for Abnormal Termination (bits 6-7 of ST0) 019F D9 EXX ; | 01A0 2804 JR Z,$01A6 ; jump if normal termination ------> --->| 01A2 10EE DJNZ $0192 ; ----> ---> ---> ---> ---> --->| 3 attempts to read a proper ID Address Mark from disk 01A4 18BB JR $0161 ; Jump to CPM_TO_COBRA | 01A6 21B5FC LD HL,$FCB5 ; <---- <---- <---- <---- <---- <---- <-| 01A9 11ABFC LD DE,$FCAB 01AC 010400 LD BC,$0004 01AF EDB0 LDIR ; copy the last 4 Read ID result bytes from $FCB2-$FCB8 to $FCAB-$FCAE (C, H, R, N) *** when done, HL=$FCB9, DE=$FCAF *** 01B1 2B DEC HL ; HL=$FCB8 01B2 46 LD B,(HL) ; B = 4th Read ID result byte: N (bytes/sector) which is 4096 for this stupidly "protected" system 01B3 2B DEC HL ; HL=$FCB7 01B4 7E LD A,(HL) ; A = 3rd Read ID result byte: R (sector number) 01B5 12 LD (DE),A ; ($FCAF)=sector number 01B6 C5 PUSH BC ; *** SAVE BC *** <---- <---- <---- <---- <---- <---- <---- <----| this loop goes N=4096 times or until no error occured while reading 01B7 AF XOR A ; A=00 | 01B8 32F0FC LD ($FCF0),A ; ($FCF0)=$00 mystery byte %%%%%%%%%%%%% ??????????????? MODIFY CODE ???????????????? %%%%%%%%%%%%%% 01BB 21F7FC LD HL,$FCF7 ; value for set CTC0 interrupt vector to, when CTC1+2_INIT is called next 01BE CD0CFD CALL $FD0C ; Call CTC1+2_INIT - sets CTC0 interrupt vector to $FCF7 | 01C1 23 INC HL ; HL=$FCF8, ($FCF8)=$A2=part of the opcode for the INI instruction | 01C2 CBDE SET 3,(HL) ; now ($FCF8)=$AA, so at $FCF7 now we have the opcode $EDAA which is the Z80 instruction IND (instead of INI) - stupid smartass way to hide the fact that system sectors are written in reverse on disk !!! 01C4 ED4BA6FC LD BC,($FCA6) ; B=$09, C=$C6 (8272 Read Data command opcode with MFM and MT(MultiTrack)) 01C8 CD2CFD CALL $FD2C ; Call 8272_CMD to send Read Data command to 8272 | 01CB 2A98FC LD HL,($FC98) ; HL=$3FFF = top address for saving system tracks data (sectors read normally but saving to mem will be done downwards, to $0000) 01CE 76 HALT ; <---- <---- <---- <---- <---- <-----| during this loop both sides of one system track (2 sectors x 4096 bytes each) are read from disk and saved to mem 01CF DBF5 IN A,($F5) ; read 8272 Status Register | | 01D1 E620 AND $20 ; test bit 5 (0 means non-DMA execution phase has ended) | 01D3 20F9 JR NZ,$01CE ; jump if not ended ----> ----> ---->| | 01D5 229AFC LD ($FC9A),HL ; save the current sector data saving address for next system track, to ($FC9A-$FC9B) 01D8 2A92FC LD HL,($FC92) ; HL=FD09 = interrupt vector for the routine call below (address of CTC1 Interrupt Service Routine) 01DB CD0CFD CALL $FD0C ; Call CTC1+2_INIT - sets CTC0 interrupt vector to $FD09 (disables CTC0 Interrupt Service Routine) 01DE CD4FFD CALL $FD4F ; Call 8272_READ and save result bytes at FCB2-FCB8 | 01E1 3AF0FC LD A,($FCF0) ; mystery byte | 01E4 B7 OR A ; test if mystery byte = 00 | 01E5 C1 POP BC ; *** RESTORE BC *** | 01E6 2805 JR Z,$01ED ; ----> ----> ----> ----> ----> ----> ---->| | 01E8 7E LD A,(HL) ; A=(FCB2) first Read Data result byte | which is ST0 | 01E9 E6D8 AND $D8 ; check error bits in ST0 | | 01EB 2805 JR Z,$01F2 ; jump if no error--> ---->| | | 01ED 10C7 DJNZ $01B6 ; ----> ----> ----> ----> ----> ----> ---->+----> ----> ----> ---->| 01EF C361FD JP $FD61 ; jump to CPM_TO_COBRA | (if reading errors occured) 01F2 21ABFC LD HL,$FCAB ; <----- <---- <---- <----| ($FCAB)=cylinder number (C, 4th Read ID result byte) 01F5 34 INC (HL) ; increment cylinder number 01F6 2A9AFC LD HL,($FC9A) ; HL = the current saving address 01F9 2298FC LD ($FC98),HL ; Overwrite the initial (top) saving address with the current one 01FC 23 INC HL ; When this routine is called the second time to read the second system track, ; at this point the current saving address in HL will be $0000 - 1 = $FFFF. ; So INC HL will bring it to $0000 since after the second call of this procedure ; a JP (HL) is executed (see the end of main code) to jumpstart the CP/M system. 01FD C9 RET 01FE 00 DEFB $00 01FF 00 DEFB $00